package cn.itmtr.document.download.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.itmtr.document.download.common.util.HtmlDownloadUtil;
import cn.itmtr.document.download.common.util.UrlProperty;
import cn.itmtr.document.download.common.validator.Assert;
import cn.itmtr.document.download.mapper.DocumentResourcesMapper;
import cn.itmtr.document.download.model.entity.Document;
import cn.itmtr.document.download.model.entity.DocumentFilter;
import cn.itmtr.document.download.model.entity.DocumentResources;
import cn.itmtr.document.download.service.IDocumentFilterService;
import cn.itmtr.document.download.service.IDocumentResourcesService;
import cn.itmtr.document.download.service.IDocumentService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.itmtr.document.download.common.util.UrlPropertyTypeEnum.GIT;
import static cn.itmtr.document.download.common.util.UrlPropertyTypeEnum.HTML;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author mtr
 * @since 2021-10-04
 */
@Slf4j
@Service
public class DocumentResourcesServiceImpl extends ServiceImpl<DocumentResourcesMapper, DocumentResources> implements IDocumentResourcesService {

    @Autowired
    public IDocumentService documentService;

    @Autowired
    public IDocumentFilterService documentFilterService;

    @Autowired
    private FreeMarkerConfigurer freeMarker;

    @Override
    public void downloadResources(Document document) {
        try {
            log.info("文档 {}[id={}]开始下载", document.getDocumentName(), document.getId());
            List<DocumentFilter> documentFilters = documentFilterService.listByDocumentId(document.getId());
            // 从入口链接开始
            UrlProperty urlProperty = HtmlDownloadUtil.resolveUrl(document.getUrl(), document.getProtocol(),
                    document.getDomain(), "/", HTML);
            downloadResourcesByUrl(document.getId(), document.getRootPath(), urlProperty, documentFilters);
            // 获取资源map
            Map<String, String> resourcesMap = this.getResourcesMap(document.getId());
            // 生成html文件
            List<DocumentResources> htmlList = this.listByType(document.getId(), HTML.getType());
            htmlList.forEach(resources -> downloadHtml(resources, resourcesMap));
            log.info("文档 {}[id={}]下载完成", document.getDocumentName(), document.getId());
        } catch (Exception e) {
            log.error("文档数据：{}", JSON.toJSONString(document));
            log.error("文档下载失败：", e);
        } finally {
            // 修改状态为已经结束
            documentService.updateStatus(document.getId(), 2);
        }
    }

    /**
     * 通过链接下载资源
     *
     * @param documentId      文档id
     * @param rootPath        根目录
     * @param urlProperty     链接数据
     * @param documentFilters 过滤列表
     * @author mtr
     * @date 2021/10/4
     */
    private void downloadResourcesByUrl(Integer documentId, String rootPath, UrlProperty urlProperty, List<DocumentFilter> documentFilters) {
        DocumentResources documentResources = this.getBySourceUrlKey(documentId, urlProperty.getSourceUrlKey());
        if (documentResources != null) {
            return;
        }
        DocumentResources resources = new DocumentResources();
        resources.setDocumentId(documentId);
        resources.setSourceUrlKey(urlProperty.getSourceUrlKey());
        resources.setSourceUrl(urlProperty.getSourceUrl());
        resources.setTargetUrl(urlProperty.getTargetUrl());
        resources.setProtocol(urlProperty.getProtocol());
        resources.setDomain(urlProperty.getDomain());
        resources.setUri(urlProperty.getUri());
        resources.setQuery(urlProperty.getQuery());
        resources.setHtmlContent("");
        resources.setHtmlTemplate("");
        resources.setType(urlProperty.getType());
        resources.setPath(rootPath + urlProperty.getPagePath());
        resources.setPagePath(urlProperty.getPagePath());
        resources.setDownloadStatus(0);
        resources.setGmtCreate(DateUtil.now());
        resources.setGmtUpdate(DateUtil.now());
        this.save(resources);

        Document document = documentService.getById(documentId);
        Assert.isNull(document, "文档不存在或已被删除");
        Assert.isTrue(document.getStatus() == 3, "文档已被手动终止");
        try {
            if (HTML.getType().equals(resources.getType())) {
                log.info("文档[id={}]保存页面：{}", resources.getDocumentId(), resources.getTargetUrl());
                // 获取网页数据
                String html = HtmlDownloadUtil.html(resources.getTargetUrl());
                // 内容过滤
                html = documentFilterService.filterDocumentHtml(documentFilters, html);
                resources.setHtmlContent(html);
                List<UrlProperty> allUrlProperty = new ArrayList<>(20);
                String filterHtml = HtmlDownloadUtil.filterUrl(html, urlProperty.getProtocol(),
                        urlProperty.getDomain(), urlProperty.getUri(), allUrlProperty);
                resources.setHtmlTemplate(filterHtml);
                resources.setDownloadStatus(1);
                resources.setGmtUpdate(DateUtil.now());
                this.updateById(resources);
                // 保存子页面
                List<UrlProperty> htmlList = new ArrayList<>(20);
                // 下载资源
                for (UrlProperty property : allUrlProperty) {
                    if (HTML.getType().equals(property.getType())) {
                        htmlList.add(property);
                    } else {
                        this.downloadResourcesByUrl(documentId, rootPath, property, documentFilters);
                    }
                }
                // 循环子页面
                for (UrlProperty property : htmlList) {
                    if (urlProperty.getDomain().equals(property.getDomain())) {
                        this.downloadResourcesByUrl(documentId, rootPath, property, documentFilters);
                    }
                }
            } else if (GIT.getType().equals(resources.getType())) {
                // 去除git
                this.removeById(resources.getId());
            } else {
                log.info("文档[id={}]下载资源：{}", resources.getDocumentId(), resources.getTargetUrl());
                String referer = document.getProtocol() + "://" + document.getDomain() + "/";
                HtmlDownloadUtil.downloadSource(resources.getTargetUrl(), resources.getPath(), referer);
                resources.setDownloadStatus(1);
                resources.setGmtUpdate(DateUtil.now());
                this.updateById(resources);
            }
        } catch (Exception e) {
            log.error("资源数据: {}", JSON.toJSONString(resources));
            log.error("资源下载出错", e);
            resources.setDownloadStatus(2);
            resources.setGmtUpdate(DateUtil.now());
            this.updateById(resources);
        }
    }

    /**
     * 下载html
     *
     * @param resources    资源数据
     * @param resourcesMap 资源路径
     * @author mtr
     * @date 2021/10/5
     */
    private void downloadHtml(DocumentResources resources, Map<String, String> resourcesMap) {
        try {
            log.info("文档[id={}]下载页面：{}", resources.getDocumentId(), resources.getTargetUrl());
            String htmlTemplate = resources.getHtmlTemplate();
            Template template = new Template(resources.getSourceUrlKey(), htmlTemplate, freeMarker.getConfiguration());
            Map<String, Object> dataMap = MapUtil.of("resourcesMap", resourcesMap);
            String result = FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap);
            FileUtil.writeString(result, resources.getPath(), "UTF-8");
        } catch (Exception e) {
            log.error("失败数据：{}", JSON.toJSONString(resources));
            log.error("下载html失败", e);
        }
    }

    @Override
    public DocumentResources getBySourceUrlKey(Integer documentId, String sourceUrlKey) {
        return this.getOne(
                new QueryWrapper<DocumentResources>()
                        .eq("document_id", documentId)
                        .eq("source_url_key", sourceUrlKey)
        );
    }

    @Override
    public Map<String, String> getResourcesMap(Integer documentId) {
        List<DocumentResources> resourcesList = this.list(
                new QueryWrapper<DocumentResources>()
                        .select("source_url_key", "page_path")
                        .eq("document_id", documentId)
                        .eq("download_status", 1)
        );
        return CollUtil.toMap(resourcesList, new HashMap<>(16), DocumentResources::getSourceUrlKey, DocumentResources::getPagePath);
    }

    @Override
    public List<DocumentResources> listByType(Integer documentId, String type) {
        return this.list(
                new QueryWrapper<DocumentResources>()
                        .eq("document_id", documentId)
                        .eq("type", type)
        );
    }

    @Override
    public void removeByDocumentId(Integer documentId) {
        this.remove(
                new QueryWrapper<DocumentResources>()
                        .eq("document_id", documentId)
        );
    }

}
